﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.IO;
using System.IO.Ports;
using System.Runtime.InteropServices;

using UniLua;

namespace SerialHelper
{
    public partial class frmMain : Form
    {
        private SerialPort serial = new SerialPort();
        private System.Timers.Timer time_timer = new System.Timers.Timer();
        private System.Timers.Timer loop_timer = new System.Timers.Timer();

        private int in_loop_timer = 0;

        private int tn_show_time = 0;
        private long count_send = 0, count_recv = 0;
        private string recv_file_path, send_file_path;

        private bool _recv_file = false;

        private bool _file_should_stop = false;
        private System.Threading.Thread file_thread = null;

        private bool _lua_should_stop = false;
        private System.Threading.Thread lua_thread = null;

        private AppConfig config = new AppConfig();

        private ILuaState lua;

        private Queue<byte> read_buffer = new Queue<byte>();

        #region 检测USB串口的热拔插
        // usb消息定义
        public const int WM_DEVICE_CHANGE = 0x219;
        public const int DBT_DEVICEARRIVAL = 0x8000;
        public const int DBT_DEVICE_REMOVE_COMPLETE = 0x8004;
        public const UInt32 DBT_DEVTYP_PORT = 0x00000003;
        [StructLayout(LayoutKind.Sequential)]
        struct DEV_BROADCAST_HDR
        {
            public UInt32 dbch_size;
            public UInt32 dbch_devicetype;
            public UInt32 dbch_reserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        protected struct DEV_BROADCAST_PORT_Fixed
        {
            public uint dbcp_size;
            public uint dbcp_devicetype;
            public uint dbcp_reserved;
            // Variable?length field dbcp_name is declared here in the C header file.
        }

       /// <summary>
       /// 检测USB串口的拔插
       /// </summary>
       /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            DEV_BROADCAST_HDR dbhdr;
            if (m.Msg == WM_DEVICE_CHANGE)        // 捕获USB设备的拔出消息WM_DEVICECHANGE
            {
                switch (m.WParam.ToInt32())
                {
                case DBT_DEVICE_REMOVE_COMPLETE:    // USB拔出  
                    dbhdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
                    if (dbhdr.dbch_devicetype == DBT_DEVTYP_PORT)
                    {
                        string portName = Marshal.PtrToStringUni((IntPtr)(m.LParam.ToInt32() + Marshal.SizeOf(typeof(DEV_BROADCAST_PORT_Fixed))));
                        Console.WriteLine("Port '" + portName + "' removed.");

                        if (portName.CompareTo(serial.PortName) == 0)
                        {
                            serial_port_close();
                        }
                    }
                    break;
                case DBT_DEVICEARRIVAL:             // USB插入获取对应串口名称
                    dbhdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
                    if (dbhdr.dbch_devicetype == DBT_DEVTYP_PORT)
                    {
                        string portName = Marshal.PtrToStringUni((IntPtr)(m.LParam.ToInt32() + Marshal.SizeOf(typeof(DEV_BROADCAST_PORT_Fixed))));
                        Console.WriteLine("Port '" + portName + "' arrived.");
                    }
                    break;
                }
            }
            base.WndProc(ref m);
        }
        #endregion

        public frmMain()
        {
            InitializeComponent();

           //////////////////////////////////////////////////////////////////////////////

            string[] checks = { "XOR异或校验", "SUM8累加校验", "ModBusLRC校验", "CRC16低字节在前", "CRC16高字节在前", "SUM16低字节在前", "SUM16高字节在前", "ModBusCRC低字节在前", "ModBusCRC高字节在前" };
            cbo_pack_check.Items.AddRange(checks);
            cbo_pack_check.Text = "XOR异或校验";

            string[] port_names = SerialPort.GetPortNames();
            Array.Sort(port_names, new CustomComparer());
            cbo_port_name.Items.AddRange(port_names);
            cbo_port_name.SelectedIndex = cbo_port_name.Items.Count > 0 ? 0 : -1;

            string[] baud_rates = { "110", "300", "600", "1200", "2400", "4800", "9600", "14400", "19200", "38400", "56000", "57600", "115200", "128000", "256000" };
            cbo_port_baud_rate.Items.AddRange(baud_rates);
            cbo_port_baud_rate.Text = "9600";

            string[] paritys = { "None", "Odd", "Even", "Mark", "Space" };
            cbo_port_parity.Items.AddRange(paritys);
            cbo_port_parity.Text = "None";

            string[] data_bits = { "5", "6", "7", "8" };
            cbo_port_data_bits.Items.AddRange(data_bits);
            cbo_port_data_bits.Text = "8";

            string[] stop_bits = { "1", "1.5", "2" };
            cbo_port_stop_bits.Items.AddRange(stop_bits);
            cbo_port_stop_bits.Text = "1";

           //////////////////////////////////////////////////////////////////////////////

            chk_recv_show.Checked = true;

           //////////////////////////////////////////////////////////////////////////////

            lua = LuaAPI.NewState();
            lua.L_OpenLibs();

            lua.RegisterGlobalFunc("read", lua_read);
            lua.RegisterGlobalFunc("write", lua_write);
            lua.RegisterGlobalFunc("send", lua_send);
            lua.RegisterGlobalFunc("sleep", lua_sleep);
            lua.RegisterGlobalFunc("show", lua_show);
            lua.RegisterGlobalFunc("help", lua_help);

           //////////////////////////////////////////////////////////////////////////////

            time_timer.Interval = 1;
            time_timer.Start();

            serial.DataReceived += serial_DataReceived;
            time_timer.Elapsed += time_timer_ElapsedEventHandler;
            loop_timer.Elapsed += loop_timer_ElapsedEventHandler;

            cbo_port_name.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
            cbo_port_baud_rate.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
            cbo_port_parity.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
            cbo_port_data_bits.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
            cbo_port_stop_bits.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
        }

        public class CustomComparer : System.Collections.IComparer
        {
            public int Compare(object x, object y)
            {
                string s1 = (string)x;
                string s2 = (string)y;

                if (s1.Length > s2.Length) return 1;
                if (s1.Length < s2.Length) return -1;
                return s1.CompareTo(s2);
            }
        }

        void serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int bytes = serial.BytesToRead;
            int i;

            try
            {
                if (bytes > 0)
                {
                    byte[] buffer = new byte[bytes]; // 声明一个临时数组存储当前来的串口数据
                    serial.Read(buffer, 0, bytes); // 读取缓冲数据

                    for (i = 0; i < bytes; i++)
                    {
                        if (read_buffer.Count() >= 65536)
                        {
                            read_buffer.Dequeue();
                        }
                        read_buffer.Enqueue(buffer[i]);
                    }

                    this.BeginInvoke((EventHandler)(delegate
                    {
                        count_recv += buffer.Length;
                        slab_recv.Text = "接收:" + count_recv.ToString();

                        if (chk_recv_show.Checked)
                        {
                            serial_recv_data(buffer);
                        }
                    }));
                }
            }
            catch (Exception ex)
            {
                this.BeginInvoke((EventHandler)(delegate
                {
                    serial_port_close();

                    MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }));
            }
        }

        void time_timer_ElapsedEventHandler(object sender, EventArgs e)
        {
            if (tn_show_time > 0)
            {
                tn_show_time--;
            }
        }

        void loop_timer_ElapsedEventHandler(object sender, EventArgs e)
        {
            if (System.Threading.Interlocked.Exchange(ref in_loop_timer, 1) == 0)
            {
                serial_send_start();

                System.Threading.Interlocked.Exchange(ref in_loop_timer, 0);
            }
        }

        private void serial_port_open()
        {
            try
            {
                if (serial.IsOpen)
                {
                    serial.Close();
                }

                serial.PortName = cbo_port_name.Text;
                serial.BaudRate = int.Parse(cbo_port_baud_rate.Text);
                serial.Parity = (Parity)Enum.Parse(typeof(Parity), cbo_port_parity.Text);
                serial.DataBits = int.Parse(cbo_port_data_bits.Text);

                StopBits[] stop_bits = { StopBits.One, StopBits.OnePointFive, StopBits.Two };
                serial.StopBits = stop_bits[cbo_port_stop_bits.SelectedIndex];

                serial.Open();
            }
            catch (Exception ex)
            {
                MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

            if (serial.IsOpen)
            {
                btn_port_open.Image = Properties.Resources.open;
                btn_port_open.Text = "     关闭串口";
            }
            else
            {
                btn_port_open.Image = Properties.Resources.close;
                btn_port_open.Text = "     打开串口";
            }
        }

        private void serial_port_close()
        {
            try
            {
                if (loop_timer.Enabled)
                {
                    send_loop_stop();
                }
                if (serial.IsOpen)
                {
                    serial.Close();
                }
            }
            catch
            {

            }

            btn_port_open.Image = Properties.Resources.close;
            btn_port_open.Text = "     打开串口";
        }

        private void btn_port_open_Click(object sender, EventArgs e)
        {
            if (serial.IsOpen)
            {
                serial_port_close();
            }
            else
            {
                serial_port_open();
            }
        }

        private void cbo_port_name_DropDown(object sender, EventArgs e)
        {
            string port_name_old = cbo_port_name.Text;

            string[] port_names = SerialPort.GetPortNames();
            Array.Sort(port_names, new CustomComparer());
            cbo_port_name.Items.Clear();
            cbo_port_name.Items.AddRange(port_names);

            cbo_port_name.Text = port_name_old;

            if ((cbo_port_name.Items.Count > 0) && (cbo_port_name.SelectedIndex < 0))
            {
                cbo_port_name.SelectedIndex = 0;
            }
        }

        private void cbo_port_SelectionChangeCommitted(object sender, EventArgs e)
        {
            if (serial.IsOpen)
            {
                serial_port_open();
            }
        }

        private void txt_show_send_KeyDown(object sender, KeyEventArgs e)
        {
            if ((e.KeyCode == Keys.Enter) && (e.Modifiers == Keys.Control))
            {
                serial_send_start();
            }
        }

        private void txt_show_recv_KeyDown(object sender, KeyEventArgs e)
        {
            if (serial.IsOpen)
            {
                if (e.KeyCode == Keys.Up)
                {
                    byte[] bytes = { 0x1B, 0x5B, 0x41 };
                    serial_send_data(bytes);
                    e.Handled = true;
                }
                else if (e.KeyCode == Keys.Down)
                {
                    byte[] bytes = { 0x1B, 0x5B, 0x42 };
                    serial_send_data(bytes);
                    e.Handled = true;
                }
                else if (e.KeyCode == Keys.Right)
                {
                    byte[] bytes = { 0x1B, 0x5B, 0x43 };
                    serial_send_data(bytes);
                    e.Handled = true;
                }
                else if (e.KeyCode == Keys.Left)
                {
                    byte[] bytes = { 0x1B, 0x5B, 0x44 };
                    serial_send_data(bytes);
                    e.Handled = true;
                }
                else if (e.KeyCode == Keys.Escape)
                {
                    byte[] bytes = { 0x1B };
                    serial_send_data(bytes);
                    e.Handled = true;
                }
                else if (e.KeyCode == Keys.Delete)
                {
                    byte[] bytes = { 0x7F };
                    serial_send_data(bytes);
                    e.Handled = true;
                }
            }
        }

        private void txt_show_recv_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (serial.IsOpen)
            {
                string str_tmp = e.KeyChar.ToString();
                byte[] bytes = Encoding.Default.GetBytes(str_tmp);
                serial_send_data(bytes);
            }

            e.Handled = true;
        }

        private void txt_show_send_clear()
        {
            if (chk_send_file.Checked)
            {
                chk_send_file.Checked = false;
            }
            else
            {
                txt_show_send.Clear();
            }
        }

        private void lbl_send_clear_Click(object sender, EventArgs e)
        {
            txt_show_send_clear();
        }

        private void txt_show_recv_clear()
        {
            if (chk_recv_file.Checked)
            {
                chk_recv_file.Checked = false;
            }
            else
            {
                txt_show_recv.Clear();
            }

            count_recv = 0;
            slab_recv.Text = "接收:" + count_recv.ToString();
            count_send = 0;
            slab_send.Text = "发送:" + count_send.ToString();
        }

        private void lbl_recv_clear_Click(object sender, EventArgs e)
        {
            txt_show_recv_clear();
        }

        private void chk_send_hex_CheckedChanged(object sender, EventArgs e)
        {
            try
            {
                if (!chk_send_file.Checked)
                {
                    if (!chk_send_lua.Checked)
                    {
                        if (chk_send_hex.Checked)
                        {
                            txt_show_send.Text = byte_to_hex(Encoding.Default.GetBytes(txt_show_send.Text));
                        }
                        else
                        {
                            txt_show_send.Text = Encoding.Default.GetString(hex_to_byte(txt_show_send.Text));
                        }
                    }
                }
            }
            catch (Exception ee)
            {
                MessageBoxEx.Show(this, ee.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private byte[] calc_modbus_crc(byte[] buffer)
        {
            UInt16 crc = 0xFFFF;
            int i, j;

            for (i = 0; i < buffer.Length; i++)
            {
                crc ^= buffer[i];
                for (j = 0; j < 8; j++)
                {
                    if ((crc & 0x0001) != 0)
                    {
                        crc >>= 1;
                        crc ^= 0xA001;
                    }
                    else
                    {
                        crc >>= 1;
                    }
                }
            }

            return BitConverter.GetBytes(crc);
        }

        private byte[] calc_crc_16(UInt16 polynomials, byte[] buffer)
        {
            UInt16 crc = 0x0000;
            int i, j;

            for (i = 0; i < buffer.Length; i++)
            {
                crc ^= (UInt16)((UInt16)buffer[i] << 8);
                for (j = 0; j < 8; j++)
                {
                    if ((crc & 0x8000) != 0)
                    {
                        crc <<= 1;
                        crc ^= polynomials;
                    }
                    else
                    {
                        crc <<= 1;
                    }
                }
            }

            return BitConverter.GetBytes(crc);
        }

        private byte[] calc_sum_16(byte[] buffer)
        {
            UInt16 sum = 0;
            int i;
            for (i = 0; i < buffer.Length; i++)
            {
                sum += buffer[i];
            }

            return BitConverter.GetBytes(sum);
        }

        private byte[] calc_xor(byte[] buffer)
        {
            byte xor = 0;
            int i;
            for (i = 0; i < buffer.Length; i++)
            {
                xor ^= buffer[i];
            }

            return new byte[] { xor };
        }

        private byte[] hex_to_byte(string str_hex)
        {
            List<byte> buffer = new List<byte>();
            int len;

            str_hex = str_hex.Trim();
            while (str_hex.Length > 0)
            {
                len = str_hex.Length;
                if (len > 2) len = 2;
                buffer.Add(byte.Parse(str_hex.Substring(0, len), System.Globalization.NumberStyles.HexNumber));
                str_hex = str_hex.Substring(len).TrimStart();
            }

            return buffer.ToArray();
        }

        private string byte_to_hex(byte[] buffer)
        {
            int i;
            string str_tmp = "";

            for (i = 0; i < buffer.Length; i++)
            {
                str_tmp += buffer[i].ToString("X2") + " ";
            }

            return str_tmp;
        }

        private void serial_recv_data(byte[] buffer)
        {
            string str_tmp = "";

            if (chk_recv_time.Checked)
            {
                if (tn_show_time == 0)
                {
                    if (txt_show_recv.Text.Length > 0)
                    {
                        str_tmp += "\r\n";
                    }
                    str_tmp += "【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "】";
                }

                tn_show_time = 10;
            }
            if (chk_recv_hex.Checked)
            {
                str_tmp += byte_to_hex(buffer);
            }
            else
            {
                str_tmp += Encoding.Default.GetString(buffer);
            }

            if (_recv_file)
            {
                File.AppendAllText(recv_file_path, str_tmp);
            }
            else
            {
                string[] strs = str_tmp.Split('\b');
                int i;

                for (i = 0; i < strs.Count(); i++)
                {
                    if ((txt_show_recv.TextLength >= 1) && (i > 0))
                    {
                        string str_select = "";
                        txt_show_recv.Select(txt_show_recv.TextLength - 1, 1);
                        if (txt_show_recv.SelectedText[0] == '\n')
                        {
                            if ((txt_show_recv.TextLength >= 2) && (txt_show_recv.Text[txt_show_recv.TextLength - 2] == '\r'))
                            {
                                txt_show_recv.Select(txt_show_recv.TextLength - 2, 2);
                            }
                        }
                        else
                        {
                            byte[] byte_tmp = Encoding.Default.GetBytes(txt_show_recv.SelectedText);
                            if (byte_tmp.Length > 1)
                            {
                                byte[] byte_select = new byte[byte_tmp.Length - 1];
                                Array.Copy(byte_tmp, byte_select, byte_select.Length);
                                str_select = Encoding.Default.GetString(byte_select);
                            }
                        }
                        txt_show_recv.SelectedText = str_select;
                    }

                    if (strs[i].LastIndexOf("\x1B[2K") >= 0)
                    {
                        strs[i] = strs[i].Substring(strs[i].LastIndexOf("\x1B[2K") + "\x1B[2K".Length);

                        int pos = txt_show_recv.Text.LastIndexOf("\r\n");
                        if (pos >= 0)
                        {
                            pos += "\r\n".Length;
                            txt_show_recv.Select(pos, txt_show_recv.Text.Length - pos);
                            txt_show_recv.SelectedText = "";
                        }
                        else
                        {
                            txt_show_recv.Text = "";
                        }
                    }

                    txt_show_recv.AppendText(strs[i]);
                }
            }
        }

        private void serial_send_data(byte[] buffer)
        {
            if (buffer.Length > 0)
            {
                if (serial.IsOpen)
                {
                    serial.Write(buffer, 0, buffer.Length);

                    this.BeginInvoke((EventHandler)(delegate
                    {
                        count_send += buffer.Length;
                        slab_send.Text = "发送:" + count_send.ToString();

                        if (chk_send_show.Checked)
                        {
                            serial_recv_data(buffer);
                        }
                    }));
                }
                else
                {
                    serial_recv_data(buffer);
                }
            }
        }

        private void send_file_thread()
        {
            FileStream file_reader = new FileStream(send_file_path, FileMode.Open, FileAccess.Read);
            long length = file_reader.Length;
            long offset = 0, count = 0;

            try
            {
                while ((!_file_should_stop) && (offset < length))
                {
                    count = length - offset;
                    if (count > 32)
                    {
                        count = 32;
                    }

                    byte[] buffer = new byte[count];
                    file_reader.Read(buffer, 0, (int)count);

                    if (chk_send_hex.Checked)
                    {
                        buffer = hex_to_byte(Encoding.Default.GetString(buffer));
                    }

                    serial_send_data(buffer);

                    offset += count;
                }
            }
            catch (Exception ex)
            {
                this.BeginInvoke((EventHandler)(delegate
                {
                    serial_send_stop();

                    MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }));
            }
            finally
            {
                file_reader.Close();

                file_thread = null;

                this.BeginInvoke((EventHandler)(delegate
                {
                    if (!chk_pack_loop.Checked)
                    {
                        btn_show_send.Text = "发送";
                    }
                }));
            }
        }

        private void serial_send_text(string text)
        {
            byte[] buffer_tmp;
            List<byte> buffer = new List<byte>();

            try
            {
                if (chk_pack_head.Checked)
                {
                    if (chk_send_hex.Checked)
                    {
                        buffer.AddRange(hex_to_byte(txt_pack_head.Text));
                    }
                    else
                    {
                        buffer.AddRange(Encoding.Default.GetBytes(txt_pack_head.Text));
                    }
                }

                if (chk_send_hex.Checked)
                {
                    buffer_tmp = hex_to_byte(text);
                }
                else
                {
                    buffer_tmp = Encoding.Default.GetBytes(text);
                }
                buffer.AddRange(buffer_tmp);

                if (chk_pack_check.Checked)
                {
                    byte[] check_buffer;
                    byte byte_tmp;
                    int check_type = 0;
                    this.Invoke((EventHandler)(delegate
                    {
                        check_type = cbo_pack_check.SelectedIndex;
                    }));
                    switch (check_type)
                    {
                    case 0: // XOR异或校验 
                        check_buffer = calc_xor(buffer_tmp);
                        buffer.AddRange(check_buffer);
                        break;
                    case 1: // SUM8校验
                        check_buffer = calc_sum_16(buffer_tmp);
                        buffer.Add(check_buffer[0]);
                        break;
                    case 2: // ModBusLRC校验
                        check_buffer = calc_sum_16(buffer_tmp);
                        buffer.AddRange(new byte[] { (byte)(-check_buffer[0]) });
                        break;
                    case 3: // CRC16低字节在前
                        check_buffer = calc_crc_16(0x1021, buffer_tmp);
                        buffer.AddRange(check_buffer);
                        break;
                    case 4: // CRC16高字节在前
                        check_buffer = calc_crc_16(0x1021, buffer_tmp);
                        byte_tmp = check_buffer[0];
                        check_buffer[0] = check_buffer[1];
                        check_buffer[1] = byte_tmp;
                        buffer.AddRange(check_buffer);
                        break;
                    case 5: // SUM16校验和低字节在前
                        check_buffer = calc_sum_16(buffer_tmp);
                        buffer.AddRange(check_buffer);
                        break;
                    case 6: // SUM16校验和高字节在前
                        check_buffer = calc_sum_16(buffer_tmp);
                        byte_tmp = check_buffer[0];
                        check_buffer[0] = check_buffer[1];
                        check_buffer[1] = byte_tmp;
                        buffer.AddRange(check_buffer);
                        break;
                    case 7: // ModBusCRC低字节在前
                        check_buffer = calc_modbus_crc(buffer_tmp);
                        buffer.AddRange(check_buffer);
                        break;
                    case 8: // ModBusCRC高字节在前
                        check_buffer = calc_modbus_crc(buffer_tmp);
                        byte_tmp = check_buffer[0];
                        check_buffer[0] = check_buffer[1];
                        check_buffer[1] = byte_tmp;
                        buffer.AddRange(check_buffer);
                        break;
                    }
                }

                if (chk_pack_end.Checked)
                {
                    if (chk_send_hex.Checked)
                    {
                        buffer.AddRange(hex_to_byte(txt_pack_end.Text));
                    }
                    else
                    {
                        buffer.AddRange(Encoding.Default.GetBytes(txt_pack_end.Text));
                    }
                }

                serial_send_data(buffer.ToArray());
            }
            catch (Exception ex)
            {
                this.BeginInvoke((EventHandler)(delegate
                {
                    serial_send_stop();

                    MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }));
            }
        }

        private int lua_read(ILuaState state)
        {
            if (read_buffer.Count() > 0)
            {
                state.PushBoolean(true);
                state.PushInteger(read_buffer.Dequeue());
            }
            else
            {
                state.PushBoolean(false);
                state.PushInteger(0);
            }

            return 2;
        }

        private int lua_write(ILuaState state)
        {
            int n_tmp = state.L_CheckInteger(1);
            byte[] buffer = { (byte)n_tmp };

            try
            {
                serial_send_data(buffer);
            }
            catch (Exception ex)
            {
                this.BeginInvoke((EventHandler)(delegate
                {
                    serial_send_stop();

                    MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }));
            }

            return 0;
        }

        private int lua_send(ILuaState state)
        {
            string text = state.L_CheckString(1);

            serial_send_text(text);

            return 0;
        }

        private int lua_sleep(ILuaState state)
        {
            int timeout = state.L_CheckInteger(1);

            System.Threading.Thread.Sleep(timeout);

            return 0;
        }

        private int lua_show(ILuaState state)
        {
            string text = state.L_CheckString(1);

            this.Invoke((EventHandler)(delegate
            {
                txt_show_recv.AppendText(text);
            }));

            return 0;
        }

        private int lua_help(ILuaState state)
        {
            this.Invoke((EventHandler)(delegate
            {
                txt_show_recv.AppendText("///////////////////////////////////////////////" + "\r\n");
                txt_show_recv.AppendText("valid,value read(void);" + "\r\n");
                txt_show_recv.AppendText("void write(int value);" + "\r\n");
                txt_show_recv.AppendText("void send(string text);" + "\r\n");
                txt_show_recv.AppendText("void sleep(int timeout);" + "\r\n");
                txt_show_recv.AppendText("void show(string text);" + "\r\n");
                txt_show_recv.AppendText("void help();" + "\r\n");
                txt_show_recv.AppendText("///////////////////////////////////////////////" + "\r\n");
            }));

            return 0;
        }

        private void send_lua_thread()
        {
            try
            {
                var status = lua.L_DoString(txt_show_send.Text);
                if (status != ThreadStatus.LUA_OK)
                {
                    throw new Exception(lua.ToString(-1));
                }
            }
            catch (Exception ex)
            {
                if (!_lua_should_stop)
                {
                    this.BeginInvoke((EventHandler)(delegate
                    {
                        serial_send_stop();

                        MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    }));
                }
            }
            finally
            {
                lua_thread = null;

                if (!_lua_should_stop)
                {
                    this.BeginInvoke((EventHandler)(delegate
                    {
                        if (!chk_pack_loop.Checked)
                        {
                            btn_show_send.Text = "发送";
                        }
                    }));
                }
            }
        }

        private void serial_send_start()
        {
            try
            {
                if (chk_send_lua.Checked)
                {
                    if (lua_thread == null)
                    {
                        lua_thread = new System.Threading.Thread(send_lua_thread);
                        _lua_should_stop = false;
                        lua_thread.Start();

                        if (!chk_pack_loop.Checked)
                        {
                            btn_show_send.Text = "停止";
                        }
                    }
                }
                else if (chk_send_file.Checked)
                {
                    if (file_thread == null)
                    {
                        file_thread = new System.Threading.Thread(send_file_thread);
                        _file_should_stop = false;
                        file_thread.Start();

                        if (!chk_pack_loop.Checked)
                        {
                            btn_show_send.Text = "停止";
                        }
                    }
                }
                else
                {
                    serial_send_text(txt_show_send.Text);

                    if (chk_send_clear.Checked)
                    {
                        txt_show_send.Clear();
                    }
                }
            }
            catch (Exception ex)
            {
                serial_send_stop();

                MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        private void send_loop_start()
        {
            loop_timer.Enabled = true;
            btn_show_send.Text = "停止";
        }

        private void send_loop_stop()
        {
            if (lua_thread != null)
            {
                _lua_should_stop = true;
                lua_thread.Abort();
                while (lua_thread != null)
                {
                    Application.DoEvents();
                }
                btn_show_send.Text = "发送";
            }
            else if (file_thread != null)
            {
                while (file_thread != null)
                {
                    _file_should_stop = true;
                    Application.DoEvents();
                }
            }

            loop_timer.Enabled = false;
            btn_show_send.Text = "发送";
        }

        private bool serial_send_stop()
        {
            if (loop_timer.Enabled)
            {
                send_loop_stop();

                return true;
            }
            else if (lua_thread != null)
            {
                _lua_should_stop = true;
                lua_thread.Abort();
                while (lua_thread != null)
                {
                    Application.DoEvents();
                }
                btn_show_send.Text = "发送";

                return true;
            }
            else if (file_thread != null)
            {
                while (file_thread != null)
                {
                    _file_should_stop = true;
                    Application.DoEvents();
                }

                return true;
            }

            read_buffer.Clear();

            return false;
        }

        private void btn_show_send_Click(object sender, EventArgs e)
        {
            if (!serial_send_stop())
            {
                if (chk_pack_loop.Checked)
                {
                    send_loop_start();
                }

                serial_send_start();
            }
        }

        private void chk_pack_loop_CheckedChanged(object sender, EventArgs e)
        {
            if (chk_pack_loop.Checked)
            {
                num_pack_loop.Enabled = false;
                loop_timer.Interval = (int)num_pack_loop.Value;
            }
            else
            {
                send_loop_stop();
                num_pack_loop.Enabled = true;
            }
        }

        private void chk_recv_file_CheckedChanged(object sender, EventArgs e)
        {
            if (chk_recv_file.Checked)
            {
                SaveFileDialog fileDialog = new SaveFileDialog();

                fileDialog.Title = "接收转向文件...";
                fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
                if (fileDialog.ShowDialog() == DialogResult.OK)
                {
                    recv_file_path = fileDialog.FileName;
                }
                else
                {
                    chk_recv_file.Checked = false;
                }
            }

            if (chk_recv_file.Checked)
            {
                txt_show_recv.Text = "文件路径: \"" + recv_file_path + "\"";
                txt_show_recv.BackColor = System.Drawing.SystemColors.Control;
            }
            else
            {
                txt_show_recv.Clear();
                txt_show_recv.BackColor = System.Drawing.SystemColors.Window;
            }

            _recv_file = chk_recv_file.Checked;
        }

        private void chk_send_file_CheckedChanged(object sender, EventArgs e)
        {
            if (chk_send_file.Checked)
            {
                OpenFileDialog fileDialog = new OpenFileDialog();

                fileDialog.Title = "发送文件数据...";
                fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
                if (fileDialog.ShowDialog() == DialogResult.OK)
                {
                    send_file_path = fileDialog.FileName;
                }
                else
                {
                    chk_send_file.Checked = false;
                }
            }

            if (chk_send_file.Checked)
            {
                txt_show_send.Text = "文件路径: \"" + send_file_path + "\"\r\n" + "文件长度: " + new FileInfo(send_file_path).Length.ToString() + "字节";
                txt_show_send.ReadOnly = true;

                chk_send_lua.Enabled = false;
                chk_send_lua.Checked = false;
            }
            else
            {
                chk_send_lua.Enabled = true;

                txt_show_send.Clear();
                txt_show_send.ReadOnly = false;

                if (file_thread != null)
                {
                    while (file_thread != null)
                    {
                        _file_should_stop = true;
                        Application.DoEvents();
                    }
                }
            }
        }

        private void slab_info_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://www.mysoow.com");  // 在浏览器中打开链接
        }

        private void lbl_recv_save_Click(object sender, EventArgs e)
        {
            SaveFileDialog fileDialog = new SaveFileDialog();

            fileDialog.Title = "保存数据";
            fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                File.WriteAllText(fileDialog.FileName, txt_show_recv.Text);
            }
        }

        private void lbl_send_load_Click(object sender, EventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();

            fileDialog.Title = "载入数据";
            fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                chk_send_file.Checked = false;
                txt_show_send.Text = File.ReadAllText(fileDialog.FileName);
            }
        }

        private void config_load()
        {
            config.Reload();

            cbo_port_name.Text = config.cbo_port_name;
            cbo_port_baud_rate.Text = config.cbo_port_baud_rate;
            cbo_port_parity.Text = config.cbo_port_parity;
            cbo_port_data_bits.Text = config.cbo_port_data_bits;
            cbo_port_stop_bits.Text = config.cbo_port_stop_bits;

            chk_recv_time.Checked = config.chk_recv_time;
            chk_recv_hex.Checked = config.chk_recv_hex;
            chk_send_show.Checked = config.chk_send_show;
            chk_recv_show.Checked = config.chk_recv_show;

            chk_send_clear.Checked = config.chk_send_clear;
            chk_send_hex.Checked = config.chk_send_hex;
            chk_send_lua.Checked = config.chk_send_lua;

            chk_pack_head.Checked = config.chk_pack_head;
            txt_pack_head.Text = config.txt_pack_head;
            chk_pack_check.Checked = config.chk_pack_check;
            cbo_pack_check.Text = config.cbo_pack_check;
            chk_pack_end.Checked = config.chk_pack_end;
            txt_pack_end.Text = config.txt_pack_end;
            num_pack_loop.Value = config.num_pack_loop;
            chk_pack_loop.Checked = config.chk_pack_loop;

            spc_back.Panel1Collapsed = config.chk_show_left;
            spc_show.Panel2Collapsed = config.chk_show_down;

            txt_show_send.Text = config.txt_show_send;
        }

        private void config_save()
        {
            config.cbo_port_name = cbo_port_name.Text;
            config.cbo_port_baud_rate = cbo_port_baud_rate.Text;
            config.cbo_port_parity = cbo_port_parity.Text;
            config.cbo_port_data_bits = cbo_port_data_bits.Text;
            config.cbo_port_stop_bits = cbo_port_stop_bits.Text;

            config.chk_recv_time = chk_recv_time.Checked;
            config.chk_recv_hex = chk_recv_hex.Checked;
            config.chk_send_show = chk_send_show.Checked;
            config.chk_recv_show = chk_recv_show.Checked;

            config.chk_send_clear = chk_send_clear.Checked;
            config.chk_send_hex = chk_send_hex.Checked;
            config.chk_send_lua = chk_send_lua.Checked;

            config.chk_pack_head = chk_pack_head.Checked;
            config.txt_pack_head = txt_pack_head.Text;
            config.chk_pack_check = chk_pack_check.Checked;
            config.cbo_pack_check = cbo_pack_check.Text;
            config.chk_pack_end = chk_pack_end.Checked;
            config.txt_pack_end = txt_pack_end.Text;
            config.chk_pack_loop = chk_pack_loop.Checked;
            config.num_pack_loop = num_pack_loop.Value;

            config.chk_show_left = spc_back.Panel1Collapsed;
            config.chk_show_down = spc_show.Panel2Collapsed;

            config.txt_show_send = txt_show_send.Text;

            config.Save();
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            config_load();
        }

        private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            serial_send_stop();
            config_save();
        }

        private void CopyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (txt_show_recv.SelectionLength > 0)
            {
                Clipboard.SetText(txt_show_recv.SelectedText);
            }
        }

        private void PasteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (serial.IsOpen)
            {
                serial_send_data(Encoding.Default.GetBytes(Clipboard.GetText()));
            }
        }

        private void SelectAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            txt_show_recv.SelectAll();
        }

        private void ClearToolStripMenuItem_Click(object sender, EventArgs e)
        {
            txt_show_recv_clear();
        }

        private void menu_show_recv_Opened(object sender, EventArgs e)
        {
            CopyToolStripMenuItem.Enabled = txt_show_recv.SelectionLength > 0;
            PasteToolStripMenuItem.Enabled = Clipboard.ContainsText();
            SelectAllToolStripMenuItem.Enabled = txt_show_recv.SelectionLength < txt_show_recv.Text.Length;
            ClearToolStripMenuItem.Enabled = txt_show_recv.TextLength > 0;
        }

        private void lbl_hide_left_Click(object sender, EventArgs e)
        {
            spc_back.Panel1Collapsed = !spc_back.Panel1Collapsed;
        }

        private void lbl_hide_left_MouseEnter(object sender, EventArgs e)
        {
            lbl_hide_left.Image = spc_back.Panel1Collapsed ? Properties.Resources.right : Properties.Resources.left;
        }

        private void lbl_hide_left_MouseLeave(object sender, EventArgs e)
        {
            lbl_hide_left.Image = null;
        }

        private void lbl_hide_down_Click(object sender, EventArgs e)
        {
            spc_show.Panel2Collapsed = !spc_show.Panel2Collapsed;
        }

        private void lbl_hide_down_MouseEnter(object sender, EventArgs e)
        {
            lbl_hide_down.Image = spc_show.Panel2Collapsed ? Properties.Resources.up : Properties.Resources.down;
        }

        private void lbl_hide_down_MouseLeave(object sender, EventArgs e)
        {
            lbl_hide_down.Image = null;
        }
    }
}
